home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / rcs55.zip / RCSLEX.C < prev    next >
C/C++ Source or Header  |  1991-09-15  |  25KB  |  981 lines

  1. /*
  2.  *                     RCS file input
  3.  */
  4. /*********************************************************************************
  5.  *                     Lexical Analysis.
  6.  *                     hashtable, Lexinit, nextlex, getlex, getkey,
  7.  *                     getid, getnum, readstring, printstring, savestring,
  8.  *                     checkid, fatserror, error, faterror, warn, diagnose
  9.  *                     Testprogram: define LEXDB
  10.  *********************************************************************************
  11.  */
  12.  
  13. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  14.    Copyright 1990 by Paul Eggert
  15.    Distributed under license by the Free Software Foundation, Inc.
  16.  
  17. This file is part of RCS.
  18.  
  19. RCS is free software; you can redistribute it and/or modify
  20. it under the terms of the GNU General Public License as published by
  21. the Free Software Foundation; either version 1, or (at your option)
  22. any later version.
  23.  
  24. RCS is distributed in the hope that it will be useful,
  25. but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27. GNU General Public License for more details.
  28.  
  29. You should have received a copy of the GNU General Public License
  30. along with RCS; see the file COPYING.  If not, write to
  31. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  32.  
  33. Report problems and direct all questions to:
  34.  
  35.     rcs-bugs@cs.purdue.edu
  36.  
  37. */
  38.  
  39.  
  40.  
  41. /* $Log: rcslex.c%v $
  42.  * Revision 1.2  1991/08/23  13:35:57  SGP
  43.  * Ported to MSDOS using Borland C++
  44.  *
  45.  * Revision 5.5  1990/12/04  05:18:47  eggert
  46.  * Use -I for prompts and -q for diagnostics.
  47.  *
  48.  * Revision 5.4  1990/11/19  20:05:28  hammer
  49.  * no longer gives warning about unknown keywords if -q is specified
  50.  *
  51.  * Revision 5.3  1990/11/01  05:03:48  eggert
  52.  * When ignoring unknown phrases, copy them to the output RCS file.
  53.  *
  54.  * Revision 5.2  1990/09/04  08:02:27  eggert
  55.  * Count RCS lines better.
  56.  *
  57.  * Revision 5.1  1990/08/29  07:14:03  eggert
  58.  * Work around buggy compilers with defective argument promotion.
  59.  *
  60.  * Revision 5.0  1990/08/22  08:12:55  eggert
  61.  * Remove compile-time limits; use malloc instead.
  62.  * Report errno-related errors with perror().
  63.  * Ansify and Posixate.  Add support for ISO 8859.
  64.  * Use better hash function.
  65.  *
  66.  * Revision 4.6  89/05/01  15:13:07  narten
  67.  * changed copyright header to reflect current distribution rules
  68.  * 
  69.  * Revision 4.5  88/08/28  15:01:12  eggert
  70.  * Don't loop when writing error messages to a full filesystem.
  71.  * Flush stderr/stdout when mixing output.
  72.  * Yield exit status compatible with diff(1).
  73.  * Shrink stdio code size; allow cc -R; remove lint.
  74.  * 
  75.  * Revision 4.4  87/12/18  11:44:47  narten
  76.  * fixed to use "varargs" in "fprintf"; this is required if it is to
  77.  * work on a SPARC machine such as a Sun-4
  78.  * 
  79.  * Revision 4.3  87/10/18  10:37:18  narten
  80.  * Updating version numbers. Changes relative to 1.1 actually relative
  81.  * to version 4.1
  82.  * 
  83.  * Revision 1.3  87/09/24  14:00:17  narten
  84.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  85.  * warnings)
  86.  * 
  87.  * Revision 1.2  87/03/27  14:22:33  jenkins
  88.  * Port to suns
  89.  * 
  90.  * Revision 4.1  83/03/25  18:12:51  wft
  91.  * Only changed $Header to $Id.
  92.  * 
  93.  * Revision 3.3  82/12/10  16:22:37  wft
  94.  * Improved error messages, changed exit status on error to 1.
  95.  *
  96.  * Revision 3.2  82/11/28  21:27:10  wft
  97.  * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
  98.  * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
  99.  * properly in case there is an IO-error (e.g., file system full).
  100.  *
  101.  * Revision 3.1  82/10/11  19:43:56  wft
  102.  * removed unused label out:;
  103.  * made sure all calls to getc() return into an integer, not a char.
  104.  */
  105.  
  106.  
  107. /*
  108. #define LEXDB
  109. */
  110. /* version LEXDB is for testing the lexical analyzer. The testprogram
  111.  * reads a stream of lexemes, enters the revision numbers into the
  112.  * hashtable, and prints the recognized tokens. Keywords are recognized
  113.  * as identifiers.
  114.  */
  115.  
  116.  
  117.  
  118. #include "rcsbase.h"
  119.  
  120. libId(lexId, "$Id: rcslex.c%v 1.2 1991/08/23 13:35:57 SGP Exp $")
  121.  
  122. static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
  123.  
  124. enum tokens     nexttok;    /*next token, set by nextlex                    */
  125.  
  126. int             hshenter;   /*if true, next suitable lexeme will be entered */
  127.                             /*into the symbol table. Handle with care.      */
  128. int             nextc;      /*next input character, initialized by Lexinit  */
  129.  
  130. unsigned long    rcsline;    /*current line-number of input            */
  131. int             nerror;     /*counter for errors                            */
  132. int             quietflag;  /*indicates quiet mode                          */
  133. FILE *          finptr;     /*input file descriptor                         */
  134.  
  135. FILE *          frewrite;   /*file descriptor for echoing input             */
  136.  
  137. FILE *        foutptr;        /* copy of frewrite, but NULL to suppress echo  */
  138.  
  139. static struct buf tokbuf;   /* token buffer                    */
  140.  
  141. const char *    NextString; /* next token                    */
  142.  
  143. /*
  144.  * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
  145.  * so hshsize should be odd.
  146.  * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
  147.  * Software--practice & experience 20, 2 (Feb 1990), 209-224.
  148.  */
  149. #ifndef hshsize
  150. #    define hshsize 511
  151. #endif
  152.  
  153. static struct hshentry *hshtab[hshsize]; /*hashtable                */
  154.  
  155. static int ignored_phrases; /* have we ignored phrases in this RCS file? */
  156.  
  157.     void
  158. warnignore()
  159. {
  160.     if (! (ignored_phrases|quietflag)) {
  161.     ignored_phrases = true;
  162.     warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
  163.     }
  164. }
  165.  
  166.  
  167.  
  168.     static void
  169. lookup(const char *str)
  170. /* Function: Looks up the character string pointed to by str in the
  171.  * hashtable. If the string is not present, a new entry for it is created.
  172.  * In any case, the address of the corresponding hashtable entry is placed
  173.  * into nexthsh.
  174.  */
  175. {
  176.     register unsigned ihash;  /* index into hashtable */
  177.     register const char *sp;
  178.     register struct hshentry *n, **p;
  179.  
  180.         /* calculate hash code */
  181.     sp = str;
  182.         ihash = 0;
  183.     while (*sp)
  184.         ihash  =  (ihash<<2) + *sp++;
  185.     ihash %= hshsize;
  186.  
  187.     for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
  188.         if (!(n = *p)) {
  189.             /* empty slot found */
  190.             *p = n = ftalloc(struct hshentry);
  191.             n->num = fstrsave(str);
  192.             n->nexthsh = nil;
  193. #            ifdef LEXDB
  194.                 VOID printf("\nEntered: %s at %u ", str, ihash);
  195. #            endif
  196.             break;
  197.         } else if (strcmp(str, n->num) == 0)
  198.             /* match found */
  199.             break;
  200.     nexthsh = n;
  201.     NextString = n->num;
  202. }
  203.  
  204.  
  205.  
  206.  
  207.  
  208.  
  209.     void
  210. Lexinit()
  211. /* Function: Initialization of lexical analyzer:
  212.  * initializes the hashtable,
  213.  * initializes nextc, nexttok if finptr != NULL
  214.  */
  215. {       register int            c;
  216.  
  217.     for (c = hshsize;  0 <= --c;  ) {
  218.         hshtab[c] = nil;
  219.         }
  220.  
  221.     hshenter=true; rcsline=1; nerror=0;
  222.     ignored_phrases = false;
  223.     bufrealloc(&tokbuf, 2);
  224.         if (finptr) {
  225.         GETC(finptr,foutptr,c);
  226.         nextc = c; /*initial character*/
  227.         nexttok = DELIM;  /* anything but EOFILE */
  228.                 nextlex();            /*initial token*/
  229.         } else {
  230.                 nextc = '\0';
  231.                 nexttok=EOFILE;
  232.         }
  233. }
  234.  
  235.  
  236.     static exiting void
  237. unexpectedEOF()
  238. {
  239.     fatserror("unexpected EOF");
  240. }
  241.  
  242.  
  243.  
  244.  
  245.  
  246.  
  247.  
  248.     void
  249. nextlex()
  250.  
  251. /* Function: Reads the next token and sets nexttok to the next token code.
  252.  * Only if hshenter is set, a revision number is entered into the
  253.  * hashtable and a pointer to it is placed into nexthsh.
  254.  * This is useful for avoiding that dates are placed into the hashtable.
  255.  * For ID's and NUM's, NextString is set to the character string.
  256.  * Assumption: nextc contains the next character.
  257.  */
  258. {       register c;
  259.     register FILE * fin, * frew;
  260.         register char * sp;
  261.     const char *lim;
  262.         register enum tokens d;
  263.  
  264.     if (nexttok == EOFILE)
  265.         unexpectedEOF();
  266.     fin=finptr; frew=foutptr;
  267.  
  268.     for (;;) switch ((nexttok=ctab[nextc])) {
  269.  
  270.     default:
  271.         fatserror("unknown character `%c'", nextc);
  272.         /*NOTREACHED*/
  273.  
  274.         case NEWLN:
  275.         ++rcsline;
  276. #               ifdef LEXDB
  277.         afputc('\n',stdout);
  278. #               endif
  279.                 /* Note: falls into next case */
  280.  
  281.         case SPACE:
  282.         GETC(fin,frew,c);
  283.         nextc = c;
  284.         continue;
  285.  
  286.         case EOFILE:
  287.                 return;
  288.  
  289.         case DIGIT:
  290.         sp = tokbuf.string;
  291.         lim = sp + tokbuf.size;
  292.                 *sp++ = nextc;
  293.         for (;;) {
  294.             GETC(fin,frew,c);
  295.             if ((d=ctab[c])!=DIGIT && d!=PERIOD)
  296.                 break;
  297.                         *sp++ = c;         /* 1.2. and 1.2 are different */
  298.             if (lim <= sp)
  299.                 sp = bufenlarge(&tokbuf, &lim);
  300.                 }
  301.         *sp = 0;
  302.                 nextc = c;
  303.         if (hshenter)
  304.             lookup(tokbuf.string);
  305.         else
  306.             NextString = fstrsave(tokbuf.string);
  307.                 nexttok = NUM;
  308.                 return;
  309.  
  310.  
  311.         case LETTER:
  312.     case Letter:
  313.         sp = tokbuf.string;
  314.         lim = sp + tokbuf.size;
  315.                 *sp++ = nextc;
  316.         for (;;) {
  317.             GETC(fin,frew,c);
  318.             if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
  319.                 break;
  320.                         *sp++ = c;
  321.             if (lim <= sp)
  322.                 sp = bufenlarge(&tokbuf, &lim);
  323.                 }
  324.         *sp = 0;
  325.                 nextc = c;
  326.         NextString = fstrsave(tokbuf.string);
  327.                 nexttok = ID;  /* may be ID or keyword */
  328.                 return;
  329.  
  330.         case SBEGIN: /* long string */
  331.                 nexttok = STRING;
  332.                 /* note: only the initial SBEGIN has been read*/
  333.                 /* read the string, and reset nextc afterwards*/
  334.                 return;
  335.  
  336.     case COLON:
  337.     case SEMI:
  338.         GETC(fin,frew,c);
  339.         nextc = c;
  340.                 return;
  341.         }
  342. }
  343.  
  344.  
  345. int getlex(enum tokens token)
  346. /* Function: Checks if nexttok is the same as token. If so,
  347.  * advances the input by calling nextlex and returns true.
  348.  * otherwise returns false.
  349.  * Doesn't work for strings and keywords; loses the character string for ids.
  350.  */
  351. {
  352.         if (nexttok==token) {
  353.                 nextlex();
  354.                 return(true);
  355.         } else  return(false);
  356. }
  357.  
  358.     int
  359. getkeyopt(const char *key)
  360. /* Function: If the current token is a keyword identical to key,
  361.  * advances the input by calling nextlex and returns true;
  362.  * otherwise returns false.
  363.  */
  364. {
  365.     if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
  366.          /* match found */
  367.          ffree1(NextString);
  368.          nextlex();
  369.          return(true);
  370.         }
  371.         return(false);
  372. }
  373.  
  374.     void
  375. getkey(const char *key)
  376. /* Check that the current input token is a keyword identical to key,
  377.  * and advance the input by calling nextlex.
  378.  */
  379. {
  380.     if (!getkeyopt(key))
  381.         fatserror("missing '%s' keyword", key);
  382. }
  383.  
  384.     void
  385. getkeystring(const char *key)
  386. /* Check that the current input token is a keyword identical to key,
  387.  * and advance the input by calling nextlex; then look ahead for a string.
  388.  */
  389. {
  390.     getkey(key);
  391.     if (nexttok != STRING)
  392.         fatserror("missing string after '%s' keyword", key);
  393. }
  394.  
  395.  
  396.     const char *
  397. getid()
  398. /* Function: Checks if nexttok is an identifier. If so,
  399.  * advances the input by calling nextlex and returns a pointer
  400.  * to the identifier; otherwise returns nil.
  401.  * Treats keywords as identifiers.
  402.  */
  403. {
  404.     register const char *name;
  405.         if (nexttok==ID) {
  406.                 name = NextString;
  407.                 nextlex();
  408.                 return name;
  409.         } else  return nil;
  410. }
  411.  
  412.  
  413. struct hshentry * getnum()
  414. /* Function: Checks if nexttok is a number. If so,
  415.  * advances the input by calling nextlex and returns a pointer
  416.  * to the hashtable entry. Otherwise returns nil.
  417.  * Doesn't work if hshenter is false.
  418.  */
  419. {
  420.         register struct hshentry * num;
  421.         if (nexttok==NUM) {
  422.                 num=nexthsh;
  423.                 nextlex();
  424.                 return num;
  425.         } else  return nil;
  426. }
  427.  
  428.     struct cbuf
  429. getphrases(const char *key)
  430. /* Get a series of phrases that do not start with KEY, yield resulting buffer.
  431.  * Stop when the next phrase starts with a token that is not an identifier,
  432.  * or is KEY.
  433.  * Assume foutptr == NULL.
  434.  */
  435. {
  436.     register FILE *fin;
  437.     register int c;
  438.     register char *p;
  439.     const char *lim;
  440.     register const char *ki, *kn;
  441.     struct cbuf r;
  442.     struct buf b;
  443.  
  444.     if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
  445.     r.string = 0;
  446.     r.size = 0;
  447.     return r;
  448.     } else {
  449.     warnignore();
  450.     fin = finptr;
  451.     bufautobegin(&b);
  452.     bufscpy(&b, NextString);
  453.     ffree1(NextString);
  454.     p = b.string + strlen(b.string);
  455.     lim = b.string + b.size;
  456.     c = nextc;
  457.     for (;;) {
  458.         for (;;) {
  459.         if (lim <= p)
  460.             p = bufenlarge(&b, &lim);
  461.         *p++ = c;
  462.         switch (ctab[c]) {
  463.             default:
  464.             fatserror("unknown character `%c'", c);
  465.             /*NOTREACHED*/
  466.             case EOFILE:
  467.             unexpectedEOF();
  468.             /*NOTREACHED*/
  469.             case NEWLN:
  470.             ++rcsline;
  471.             /* fall into */
  472.             case COLON: case DIGIT: case LETTER: case Letter:
  473.             case PERIOD: case SPACE:
  474.             c = getc(fin);
  475.             continue;
  476.             case SBEGIN: /* long string */
  477.             for (;;) {
  478.                 for (;;) {
  479.                 c = getc(fin);
  480.                 if (lim <= p)
  481.                     p = bufenlarge(&b, &lim);
  482.                 *p++ = c;
  483.                 switch (c) {
  484.                     case EOF:
  485.                     unexpectedEOF();
  486.                     /*NOTREACHED*/
  487.                     case '\n':
  488.                     ++rcsline;
  489.                     /* fall into */
  490.                     default:
  491.                     continue;
  492.                     case SDELIM:
  493.                     break;
  494.                 }
  495.                 break;
  496.                 }
  497.                 c = getc(fin);
  498.                 if (c != SDELIM)
  499.                 break;
  500.                 if (lim <= p)
  501.                 p = bufenlarge(&b, &lim);
  502.                 *p++ = c;
  503.             }
  504.             continue;
  505.             case SEMI:
  506.             c = getc(fin);
  507.             if (ctab[c] == NEWLN) {
  508.                 ++rcsline;
  509.                 if (lim <= p)
  510.                 p = bufenlarge(&b, &lim);
  511.                 *p++ = c;
  512.                 c = getc(fin);
  513.             }
  514.             for (;; c = getc(fin)) {
  515.                 switch (ctab[c]) {
  516.                 case NEWLN: ++rcsline; continue;
  517.                 case SPACE: continue;
  518.                 default: break;
  519.                 }
  520.                 break;
  521.             }
  522.             break;
  523.         }
  524.         break;
  525.         }
  526.         switch (ctab[c]) {
  527.         case LETTER:
  528.         case Letter:
  529.             for (kn = key;  c && *kn==c;  kn++)
  530.             if ((c = getc(fin)) == EOF)
  531.                 unexpectedEOF();
  532.             if (!*kn)
  533.             switch (ctab[c]) {
  534.                 case DIGIT: case LETTER: case Letter:
  535.                 break;
  536.                 default:
  537.                 nextc = c;
  538.                 NextString = fstrsave(key);
  539.                 nexttok = ID;
  540.                 goto returnit;
  541.             }
  542.             for (ki=key; ki<kn; ) {
  543.             if (lim <= p)
  544.                 p = bufenlarge(&b, &lim);
  545.             *p++ = *ki++;
  546.             }
  547.             break;
  548.         default:
  549.             nextc = c;
  550.             nextlex();
  551.             goto returnit;
  552.         }
  553.     }
  554.  
  555.     returnit:
  556.     /*
  557.      * Do the following instead of bufautoend(&b),
  558.      * because the buffer must survive until we are done with the file.
  559.      */
  560.     r.size = p - b.string;
  561.     r.string = (char*)fremember(testrealloc((malloc_type)b.string, r.size));
  562.     return r;
  563.     }
  564. }
  565.  
  566.  
  567.     void
  568. readstring()
  569. /* skip over characters until terminating single SDELIM        */
  570. /* If foutptr is set, copy every character read to foutptr.    */
  571. /* Does not advance nextlex at the end.                        */
  572. {       register c;
  573.     register FILE * fin,  * frew;
  574.     fin=finptr; frew=foutptr;
  575.     if (frew) {
  576.         /* Copy string verbatim to foutptr.  */
  577.                 while ((c=getc(fin)) != EOF) {
  578.             aputc(c,frew);
  579.             switch (c) {
  580.                 case '\n':
  581.                 ++rcsline;
  582.                 break;
  583.                 case SDELIM:
  584.                 if ((c=getc(fin)) == EOF) {
  585.                     nextc=c;
  586.                     return;
  587.                 }
  588.                 aputc(c,frew);
  589.                 if (c != SDELIM) {
  590.                                         /* end of string */
  591.                                         nextc=c;
  592.                                         return;
  593.                                 }
  594.                 break;
  595.                         }
  596.                 }
  597.         } else {
  598.                 /* skip string */
  599.                 while ((c=getc(fin)) != EOF) {
  600.             switch (c) {
  601.                 case '\n':
  602.                 ++rcsline;
  603.                 break;
  604.                 case SDELIM:
  605.                                 if ((c=getc(fin)) != SDELIM) {
  606.                                         /* end of string */
  607.                                         nextc=c;
  608.                                         return;
  609.                                 }
  610.                 break;
  611.                         }
  612.                 }
  613.         }
  614.     unterminatedString();
  615. }
  616.  
  617.  
  618.     void
  619. printstring()
  620. /* Function: copy a string to stdout, until terminated with a single SDELIM.
  621.  * Does not advance nextlex at the end.
  622.  */
  623. {
  624.         register c;
  625.     register FILE *fin, *fout;
  626.     fin=finptr;
  627.     fout = stdout;
  628.     while ((c=getc(fin)) != EOF) {
  629.         switch (c) {
  630.             case '\n':
  631.             ++rcsline;
  632.             break;
  633.             case SDELIM:
  634.             if ((c=getc(fin)) != SDELIM) {
  635.                                 /* end of string */
  636.                                 nextc=c;
  637.                                 return;
  638.                         }
  639.             break;
  640.                 }
  641.         aputc(c,fout);
  642.         }
  643.     unterminatedString();
  644. }
  645.  
  646.  
  647.  
  648.     struct cbuf
  649. savestring(struct buf *target)
  650. /* Copies a string terminated with SDELIM from file finptr to buffer target.
  651.  * Double SDELIM is replaced with SDELIM.
  652.  * If foutptr is set, the string is also copied unchanged to foutptr.
  653.  * Does not advance nextlex at the end.
  654.  * Yield a copy of *TARGET, except with exact length.
  655.  */
  656. {
  657.         register c;
  658.     register FILE * fin, * frew;
  659.     register char *tp;
  660.     const char *lim;
  661.     struct cbuf r;
  662.  
  663.     fin=finptr; frew=foutptr;
  664.     tp = target->string;  lim = tp + target->size;
  665.     for (;;) {
  666.         GETC(fin,frew,c);
  667.         switch (c) {
  668.             case '\n':
  669.             ++rcsline;
  670.             break;
  671.             case SDELIM:
  672.             GETC(fin,frew,c);
  673.             if (c != SDELIM) {
  674.                                 /* end of string */
  675.                                 nextc=c;
  676.                 r.string = target->string;
  677.                 r.size = tp - r.string;
  678.                 return r;
  679.                         }
  680.             break;
  681.             case EOF:
  682.             unterminatedString();
  683.                 }
  684.         if (tp == lim)
  685.             tp = bufenlarge(target, &lim);
  686.         *tp++ = c;
  687.         }
  688. }
  689.  
  690.  
  691.     char *
  692. checkid(register char *id, int delimiter)
  693. /*   Function:  check whether the string starting at id is an   */
  694. /*        identifier and return a pointer to the delimiter*/
  695. /*        after the identifier.  White space, delim and 0 */
  696. /*              are legal delimiters.  Aborts the program if not*/
  697. /*              a legal identifier. Useful for checking commands*/
  698. /*        If !delim, the only delimiter is 0.        */
  699. {
  700.         register enum  tokens  d;
  701.         register char    *temp;
  702.         register char    c,tc;
  703.     register char delim = delimiter;
  704.  
  705.     temp = id;
  706.     if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) {
  707.         while ((d = ctab[(unsigned char)(c = *++id)])==LETTER
  708.         || d==Letter || d==DIGIT || d==IDCHAR
  709.         )
  710.         ;
  711.         if (c  &&  (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) {
  712.                 /* append \0 to end of id before error message */
  713.                 tc = c;
  714.                 while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  715.                 *id = '\0';
  716.         faterror("invalid character %c in identifier `%s'",tc,temp);
  717.         }
  718.         } else {
  719.             /* append \0 to end of id before error message */
  720.             while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  721.             *id = '\0';
  722.         faterror("identifier `%s' doesn't start with letter", temp);
  723.         }
  724.     return id;
  725. }
  726.  
  727.     void
  728. checksid(register char *id)
  729. /* Check whether the string ID is an identifier.  */
  730. {
  731.     VOID checkid(id, 0);
  732. }
  733.  
  734.  
  735.     exiting void
  736. IOerror()
  737. {
  738.     static looping;
  739.     if (looping)
  740.         exiterr();
  741.     looping = true;
  742.     faterror("input/output error; is the file system full?");
  743. }
  744.  
  745. void eflush() { if (fflush(stderr) == EOF) IOerror(); }
  746. void oflush() { if (fflush(stdout) == EOF) IOerror(); }
  747.  
  748. exiting void unterminatedString() { fatserror("unterminated string"); }
  749.  
  750.     static exiting void
  751. fatcleanup(int already_newline)
  752. {
  753.     VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
  754.     exiterr();
  755. }
  756.  
  757. static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
  758. static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
  759.  
  760. void eerror(const char *n) { errsay(); perror(n); eflush(); }
  761. exiting void efaterror(const char *n) { fatsay(); perror(n); fatcleanup(true); }
  762.  
  763. #if has_prototypes
  764.     void
  765. error(const char *format,...)
  766. #else
  767.     /*VARARGS1*/ void error(format, va_alist) const char *format; va_dcl
  768. #endif
  769. /* non-fatal error */
  770. {
  771.     va_list args;
  772.     errsay();
  773.     vararg_start(args, format);
  774.     fvfprintf(stderr, format, args);
  775.     va_end(args);
  776.     afputc('\n',stderr);
  777.     eflush();
  778. }
  779.  
  780. #if has_prototypes
  781.     exiting void
  782. fatserror(const char *format,...)
  783. #else
  784.     /*VARARGS1*/ exiting void
  785.     fatserror(format, va_alist) const char *format; va_dcl
  786. #endif
  787. /* fatal syntax error */
  788. {
  789.     va_list args;
  790.     oflush();
  791.     VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline);
  792.     vararg_start(args, format);
  793.     fvfprintf(stderr, format, args);
  794.     va_end(args);
  795.     fatcleanup(false);
  796. }
  797.  
  798. #if has_prototypes
  799.     exiting void
  800. faterror(const char *format,...)
  801. #else
  802.     /*VARARGS1*/ exiting void faterror(format, va_alist)
  803.     const char *format; va_dcl
  804. #endif
  805. /* fatal error, terminates program after cleanup */
  806. {
  807.     va_list args;
  808.     fatsay();
  809.     vararg_start(args, format);
  810.     fvfprintf(stderr, format, args);
  811.     va_end(args);
  812.     fatcleanup(false);
  813. }
  814.  
  815. #if has_prototypes
  816.     void
  817. warn(const char *format,...)
  818. #else
  819.     /*VARARGS1*/ void warn(format, va_alist) const char *format; va_dcl
  820. #endif
  821. /* prints a warning message */
  822. {
  823.     va_list args;
  824.     oflush();
  825.     aprintf(stderr,"%s warning: ",cmdid);
  826.     vararg_start(args, format);
  827.     fvfprintf(stderr, format, args);
  828.     va_end(args);
  829.     afputc('\n',stderr);
  830.     eflush();
  831. }
  832.  
  833.     void
  834. redefined(int c)
  835. {
  836.     warn("redefinition of -%c option", c);
  837. }
  838.  
  839. #if has_prototypes
  840.     void
  841. diagnose(const char *format,...)
  842. #else
  843.     /*VARARGS1*/ void diagnose(format, va_alist) const char *format; va_dcl
  844. #endif
  845. /* prints a diagnostic message */
  846. /* Unlike the other routines, it does not append a newline. */
  847. /* This lets some callers suppress the newline, and is faster */
  848. /* in implementations that flush stderr just at the end of each printf. */
  849. {
  850.     va_list args;
  851.         if (!quietflag) {
  852.         oflush();
  853.         vararg_start(args, format);
  854.         fvfprintf(stderr, format, args);
  855.         va_end(args);
  856.         eflush();
  857.         }
  858. }
  859.  
  860.  
  861.  
  862.     void
  863. afputc(int c, register FILE *f)
  864. /* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower.
  865.  */
  866. {
  867.     aputc(c,f);
  868. }
  869.  
  870.  
  871.     void
  872. aputs(const char *s, FILE *iop)
  873. /* Function: Put string s on file iop, abort on error.
  874.  */
  875. {
  876.     if (fputs(s, iop) == EOF)
  877.         IOerror();
  878. }
  879.  
  880.  
  881.  
  882.     void
  883. #if has_prototypes
  884. fvfprintf(FILE *stream, const char *format, va_list args)
  885. #else
  886.     fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
  887. #endif
  888. /* like vfprintf, except abort program on error */
  889. {
  890. #if has_vfprintf
  891.     if (vfprintf(stream, format, args) == EOF)
  892. #else
  893.     _doprnt(format, args, stream);
  894.     if (ferror(stream))
  895. #endif
  896.         IOerror();
  897. }
  898.  
  899. #if has_prototypes
  900.     void
  901. aprintf(FILE *iop, const char *fmt, ...)
  902. #else
  903.     /*VARARGS2*/ void
  904. aprintf(iop, fmt, va_alist)
  905. FILE *iop;
  906. const char *fmt;
  907. va_dcl
  908. #endif
  909. /* Function: formatted output. Same as fprintf in stdio,
  910.  * but aborts program on error
  911.  */
  912. {
  913.     va_list ap;
  914.     vararg_start(ap, fmt);
  915.     fvfprintf(iop, fmt, ap);
  916.     va_end(ap);
  917. }
  918.  
  919.  
  920.  
  921. #ifdef LEXDB
  922. /* test program reading a stream of lexemes and printing the tokens.
  923.  */
  924.  
  925.  
  926.  
  927.     int
  928. main(int argc,char *argv[])
  929. {
  930.         cmdid="lextest";
  931.         if (argc<2) {
  932.         aputs("No input file\n",stderr);
  933.         exitmain(EXIT_FAILURE);
  934.         }
  935.         if ((finptr=fopen(argv[1], "r")) == NULL) {
  936.         faterror("can't open input file %s",argv[1]);
  937.         }
  938.         Lexinit();
  939.         while (nexttok != EOFILE) {
  940.         switch (nexttok) {
  941.  
  942.         case ID:
  943.                 VOID printf("ID: %s",NextString);
  944.                 break;
  945.  
  946.         case NUM:
  947.         if (hshenter)
  948.                    VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
  949.                 else
  950.                    VOID printf("NUM, unentered: %s",NextString);
  951.                 hshenter = !hshenter; /*alternate between dates and numbers*/
  952.                 break;
  953.  
  954.         case COLON:
  955.                 VOID printf("COLON"); break;
  956.  
  957.         case SEMI:
  958.                 VOID printf("SEMI"); break;
  959.  
  960.         case STRING:
  961.                 readstring();
  962.                 VOID printf("STRING"); break;
  963.  
  964.         case UNKN:
  965.                 VOID printf("UNKN"); break;
  966.  
  967.         default:
  968.                 VOID printf("DEFAULT"); break;
  969.         }
  970.         VOID printf(" | ");
  971.         nextlex();
  972.         }
  973.         VOID printf("\nEnd of lexical analyzer test\n");
  974.     exitmain(EXIT_SUCCESS);
  975. }
  976.  
  977. exiting void exiterr() { _exit(EXIT_FAILURE); }
  978.  
  979.  
  980. #endif
  981.